package com.share.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.share.common.core.domain.R;
import com.share.common.core.exception.ServiceException;
import com.share.common.core.utils.StringUtils;
import com.share.common.security.utils.SecurityUtils;
import com.share.order.domain.*;
import com.share.order.mapper.OrderBillMapper;
import com.share.order.mapper.OrderInfoMapper;
import com.share.order.service.IOrderInfoService;
import com.share.rule.api.RemoteFeeRuleService;
import com.share.rule.domain.FeeRule;
import com.share.rule.domain.FeeRuleRequestForm;
import com.share.rule.domain.FeeRuleResponseVo;
import com.share.user.api.RemoteUserService;
import com.share.user.domain.UserInfo;
import lombok.RequiredArgsConstructor;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

/**
 * 订单Service业务层处理
 */
@Service
@RequiredArgsConstructor
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements IOrderInfoService {
    private final RemoteFeeRuleService remoteFeeRuleService;
    private final RemoteUserService remoteUserInfoService;
    private final OrderBillMapper orderBillMapper;

    // 根据订单号查询订单信息
    @Override
    public OrderInfo getByOrderNo(String orderNo) {
        return baseMapper.selectOne(
                new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getOrderNo, orderNo)
        );
    }

    // 修改订单状态
    @Override
    public void processPaySuccess(String orderNo) {
        //获取订单信息
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getOrderNo, orderNo);
        OrderInfo orderInfo = baseMapper.selectOne(wrapper);

        // 仅当订单状态为“未支付（1）”时，更新为“已支付（2）”
        if (orderInfo != null && "1".equals(orderInfo.getStatus())) {
            orderInfo.setStatus("2");
            orderInfo.setPayTime(new Date());
            baseMapper.updateById(orderInfo);
        }
    }

    // 传递过来sql语句，根据sql语句查询数据库得到报表数据
    @Override
    public Map<String, Object> getOrderCount(String sql) {
        //调用mapper方法执行sql语句
        List<Map<String, Object>> list = baseMapper.getOrderCount(sql);
        Map<String, Object> dataMap = new HashMap<>();

        List<Object> dateList = new ArrayList<>();
        List<Object> countList = new ArrayList<>();

        //把list集合遍历，得到每个map
        for (Map<String, Object> map : list) {
            //把每个map里面日期得到放到新的list集合里面
            dateList.add(map.get("order_date"));
            //把每个map里面数量得到放到新的list集合里面
            countList.add(map.get("order_count"));
        }
        //把两个list集合放到dataMap中，返回
        dataMap.put("dateList", dateList);
        dataMap.put("countList", countList);
        return dataMap;
    }

    // 获取未完成订单
    @Override
    public OrderInfo getNoFinishOrder(Long userId) {
        // 查询用户是否有使用中与未支付订单
        return baseMapper.selectOne(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getUserId, userId)
                .in(OrderInfo::getStatus, Arrays.asList("0", "1"))// 订单状态：0:充电中 1：未支付 2：已支付
                .orderByDesc(OrderInfo::getId)
                .last("limit 1")
        );
    }

    // 生成订单
    @Override
    public Long saveOrder(SubmitOrderVo submitOrderVo) {
        OrderInfo orderInfo = new OrderInfo();
        //设置需要数据
        orderInfo.setUserId(submitOrderVo.getUserId());
        orderInfo.setOrderNo(RandomUtil.randomString(8));
        orderInfo.setPowerBankNo(submitOrderVo.getPowerBankNo());
        orderInfo.setStartTime(new Date());
        orderInfo.setStartStationId(submitOrderVo.getStartStationId());
        orderInfo.setStartStationName(submitOrderVo.getStartStationName());
        orderInfo.setStartCabinetNo(submitOrderVo.getStartCabinetNo());
        // 费用规则
        R<FeeRule> feeRuleR = remoteFeeRuleService.getFeeRule(submitOrderVo.getFeeRuleId());
        if (R.FAIL == feeRuleR.getCode() || feeRuleR.getData() == null) {
            throw new ServiceException("获取费用规则失败");
        }
        FeeRule feeRule = feeRuleR.getData();
        orderInfo.setFeeRuleId(submitOrderVo.getFeeRuleId());
        orderInfo.setFeeRule(feeRule.getDescription());
        orderInfo.setStatus("0");
        orderInfo.setCreateTime(new Date());
        orderInfo.setCreateBy(SecurityUtils.getUsername());
        //用户昵称
        R<UserInfo> userInfoR = remoteUserInfoService.getInfo(submitOrderVo.getUserId());
        if (R.FAIL == userInfoR.getCode() || userInfoR.getData() == null) {
            throw new ServiceException("获取用户信息失败");
        }
//         orderInfo.setNickname(userInfo.getNickname());
        orderInfo.setUserInfoVo(BeanUtil.copyProperties(userInfoR.getData(), UserInfoVo.class));

        baseMapper.insert(orderInfo);
        return orderInfo.getId();
    }

    // 结束订单
    @Override
    public void endOrder(EndOrderVo endOrderVo) {
        // 1. 查询当前充电宝的“充电中”订单
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getPowerBankNo, endOrderVo.getPowerBankNo())
                .eq(OrderInfo::getStatus, "0"); // 仅查询充电中订单
        OrderInfo orderInfo = baseMapper.selectOne(wrapper);
        if (orderInfo == null) {
            return; // 无充电中订单，直接返回
        }

        // 2. 计算充电时长（分钟）
        Date startTime = orderInfo.getStartTime();
        Date endTime = endOrderVo.getEndTime();
        int duration = Minutes.minutesBetween(new DateTime(startTime), new DateTime(endTime)).getMinutes();

        // 3. 远程调用规则引擎计算费用
        FeeRuleResponseVo feeResponse = calculateOrderFee(orderInfo.getFeeRuleId(), duration);

        // 4. 更新订单信息
        orderInfo.setEndTime(endTime);
        orderInfo.setEndStationId(endOrderVo.getEndStationId());
        orderInfo.setEndStationName(endOrderVo.getEndStationName());
        orderInfo.setEndCabinetNo(endOrderVo.getEndCabinetNo());
        orderInfo.setDuration(duration);
        orderInfo.setTotalAmount(feeResponse.getTotalAmount());
        orderInfo.setDeductAmount(new BigDecimal("0"));
        orderInfo.setRealAmount(feeResponse.getTotalAmount());
        // 金额为0直接设为“已支付”，否则设为“未支付”
        orderInfo.setStatus(feeResponse.getTotalAmount().compareTo(BigDecimal.ZERO) == 0 ? "2" : "1");
        baseMapper.updateById(orderInfo);

        // 5. 插入订单账单（免费项+收费项）
        insertOrderBill(orderInfo.getId(), feeResponse);
    }

    //获取用户订单分页列表
    @Override
    public List<OrderInfo> selectOrderListByUserId(Long userId) {
        //1 根据用户id查询用户所有订单列表，返回list集合
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getUserId, userId);
        wrapper.orderByDesc(OrderInfo::getId);
        List<OrderInfo> list = baseMapper.selectList(wrapper);

        if (list.isEmpty()) return Collections.emptyList();

        //2 遍历list集合，得到每个orderInfo对象
        for (OrderInfo orderInfo : list) {
            //3 判断订单状态是0（充电中），实时计算充电时间和金额
            processChargingOrder(orderInfo, false); // false：不需要账单和用户信息
        }
        return list;
    }

    // 获取订单详细信息
    @Override
    public OrderInfo selectOrderInfoById(Long id) {
        // 根据id查询订单信息
        OrderInfo orderInfo = baseMapper.selectById(id);
        if (orderInfo == null) return null;

        // 处理充电中订单的实时数据，同时加载账单和用户信息
        processChargingOrder(orderInfo, true); // true：需要账单和用户信息
        return orderInfo;
    }

    // 根据用户ID获取所有订单详情（含账单+用户信息）
    @Override
    public List<OrderInfo> selectOrderInfos(Long userId) {
        // 查询用户所有订单
        List<OrderInfo> orderList = lambdaQuery()
                .eq(OrderInfo::getUserId, userId)
                .list();

        if (orderList.isEmpty()) {
            return Collections.emptyList();
        }

        // 处理每个订单：更新充电中数据+加载账单和用户信息
        List<OrderInfo> resultList = new ArrayList<>();
        for (OrderInfo orderInfo : orderList) {
            processChargingOrder(orderInfo, true); // true：需要账单和用户信息
            resultList.add(orderInfo);
        }

        return resultList;
    }

    // ------------------------------ 以下是提取的公共方法 ------------------------------

    /**
     * 处理“充电中”订单的实时数据（时长+金额），可选加载账单和用户信息
     * @param orderInfo 待处理的订单对象
     * @param needExtraInfo 是否需要加载额外信息（账单+用户信息）：true=需要，false=不需要
     */
    private void processChargingOrder(OrderInfo orderInfo, boolean needExtraInfo) {
        // 1. 仅处理“充电中（0）”状态的订单，计算实时时长和金额
        if ("0".equals(orderInfo.getStatus())) {
            // 计算当前充电时长（当前时间 - 订单开始时间）
            int minutes = Minutes.minutesBetween(
                    new DateTime(orderInfo.getStartTime()),
                    new DateTime() // 当前时间
            ).getMinutes();

            if (minutes > 0) {
                orderInfo.setDuration(minutes);
                // 远程调用计算费用
                FeeRuleResponseVo feeResponse = calculateOrderFee(orderInfo.getFeeRuleId(), minutes);
                orderInfo.setTotalAmount(feeResponse.getTotalAmount());
                orderInfo.setDeductAmount(new BigDecimal("0"));
                orderInfo.setRealAmount(feeResponse.getTotalAmount());
            } else {
                // 时长为0时，金额默认设为0
                orderInfo.setDuration(0);
                orderInfo.setTotalAmount(BigDecimal.ZERO);
                orderInfo.setDeductAmount(BigDecimal.ZERO);
                orderInfo.setRealAmount(BigDecimal.ZERO);
            }
        }

        // 2. 如需额外信息，加载订单账单和用户信息
        if (needExtraInfo) {
            // 加载订单账单（修复原逻辑：用订单ID查询，而非用户ID）
            List<OrderBill> orderBillList = orderBillMapper.selectList(
                    new LambdaQueryWrapper<OrderBill>().eq(OrderBill::getOrderId, orderInfo.getId())
            );
            orderInfo.setOrderBillList(orderBillList);

            // 加载用户信息（远程调用）
            loadUserInfoToOrder(orderInfo);
        }
    }

    /**
     * 远程调用规则引擎计算订单费用（公共方法，避免重复调用逻辑）
     * @param feeRuleId 费用规则ID
     * @param duration 充电时长（分钟）
     * @return 费用计算结果
     */
    private FeeRuleResponseVo calculateOrderFee(Long feeRuleId, int duration) {
        FeeRuleRequestForm requestForm = new FeeRuleRequestForm();
        requestForm.setFeeRuleId(feeRuleId);
        requestForm.setDuration(duration);

        R<FeeRuleResponseVo> feeResponseR = remoteFeeRuleService.calculateOrderFee(requestForm);
        if (R.FAIL == feeResponseR.getCode() || feeResponseR.getData() == null) {
            throw new ServiceException(
                    StringUtils.isNotBlank(feeResponseR.getMsg())
                            ? feeResponseR.getMsg()
                            : "订单费用计算失败"
            );
        }
        return feeResponseR.getData();
    }

    /**
     * 加载用户信息到订单对象（公共方法，避免重复调用逻辑）
     * @param orderInfo 待填充用户信息的订单对象
     */
    private void loadUserInfoToOrder(OrderInfo orderInfo) {
        R<UserInfo> userInfoR = remoteUserInfoService.getInfo(orderInfo.getUserId());
        if (StringUtils.isNull(userInfoR) || StringUtils.isNull(userInfoR.getData())) {
            throw new ServiceException("获取用户信息失败");
        }
        if (R.FAIL == userInfoR.getCode()) {
            throw new ServiceException(userInfoR.getMsg());
        }

        UserInfoVo userInfoVo = new UserInfoVo();
        BeanUtils.copyProperties(userInfoR.getData(), userInfoVo);
        orderInfo.setUserInfoVo(userInfoVo);
    }

    /**
     * 插入订单账单（免费项+收费项）（公共方法，仅在结束订单时调用）
     * @param orderId 订单ID
     * @param feeResponse 费用计算结果（含账单描述）
     */
    private void insertOrderBill(Long orderId, FeeRuleResponseVo feeResponse) {
        // 1. 插入免费账单（无论是否有收费项，都需插入免费项）
        OrderBill freeBill = new OrderBill();
        freeBill.setOrderId(orderId);
        freeBill.setBillItem(feeResponse.getFreeDescription());
        freeBill.setBillAmount(BigDecimal.ZERO);
        orderBillMapper.insert(freeBill);

        // 2. 如有超出免费时长的费用，插入收费账单
        BigDecimal exceedPrice = feeResponse.getExceedPrice();
        if (exceedPrice != null && exceedPrice.compareTo(BigDecimal.ZERO) > 0) {
            OrderBill exceedBill = new OrderBill();
            exceedBill.setOrderId(orderId);
            exceedBill.setBillItem(feeResponse.getExceedDescription());
            exceedBill.setBillAmount(exceedPrice);
            orderBillMapper.insert(exceedBill);
        }
    }

}
